#include <assert.h>
#include <cassert>
#include <stdexcept>
#include <string.h>

#include "../headers/Comparison.h"
#include "../headers/Helpers.h"

using namespace std;

void convert_to_mix_bytes(const long long value, unsigned char* const buffer, const int bufferSize)
{
	unsigned long long toSet = abs(value);

	unsigned int mod;
	for(int n = bufferSize - 1; n >= 0; n--)
	{
		mod = toSet % BaseWord::DISTINCT_BYTE_VALUES;
		buffer[n] = mod;

		toSet -= mod;

		assert(toSet % BaseWord::DISTINCT_BYTE_VALUES == 0);
		toSet /= BaseWord::DISTINCT_BYTE_VALUES;
	}

	// NOTE: we allow the operation to set whatever it needs to set
	//       and forget those pieces we couldn't
	if(toSet != 0)
	{
		throw overflow_error("The value is too large for the word.");
	}
}


// Converts a hexadecimal string to integer
// input: input string
// value: integer value representing input string
int xtoi(const char* input, unsigned int* value)
{
	size_t length = strlen(input);

	if(length > 0)
	{
		// Converting more than 32bit hexadecimal value?
		if(length > 8)
		{
			return -1;
		}

		// Begin conversion here
		*value = 0;
		int factor = 1;
		int current;

		// Run until no more character to convert
		for(int i = length - 1; i >= 0; i--)
		{
			if(isxdigit(*(input + i)))
			{
				if(*(input + i) >= 97)
				{
					current = (*(input + i) - 97) + 10;
				}
				else if(*(input + i) >= 65)
				{
					current = (*(input + i) - 65) + 10;
				}
				else
				{
					current = *(input + i) - 48;
				}

				*value += (current * factor);
				factor *= 0x10;
			}
			else
			{
				// Conversion was abnormally terminated
				// by non hexadecimal digit, hence
				// returning only the converted with
				// an error value 4 (illegal hex character)
				return -1;
			}
		}
	}

	return 0;
}


int strcmpci(const char* const left, const char* const right)
{
	int result = 0;
	int index = 0;

	while(tolower(*(left + index)) == tolower(*(right + index)) && *(left + index) != '\0' && *(right + index) != '\0')
	{
		index++;
	}

	if(*(left + index) == '\0' && *(right + index) != '\0')
	{
		result = -1;
	}
	else if(*(left + index) != '\0' && *(right + index) == '\0')
	{
		result = 1;
	}
	else if(*(left + index) == '\0' && *(right + index) == '\0')
	{
		result = 0;
	}
	else
	{
		result = (*(left + index) > *(right + index) ? -1 : 1);
	}

	return result;
}


void pretty_print_comparison(std::ostream* const os, Comparison comparison)
{
	switch (comparison)
	{
		case Less:
			(*os) << "less";
			break;

		case Equal:
			(*os) << "equal";
			break;

		case Greater:
			(*os) << "greater";
			break;

		default:
			assert(false);
			break;
	}
}
